package com.gxa.modules.shiro.global;
import com.gxa.modules.mapper.SysRoleMapper;
import com.gxa.modules.mapper.SysUserMapper;
import com.gxa.modules.pojo.Sys_Menu;
import com.gxa.modules.pojo.Sys_Role;
import com.gxa.modules.pojo.Sys_User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 自定义Realm
 */
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录成功后的账户名 -- 根据账户名查询对应的账户id，
        String username =  principalCollection.getPrimaryPrincipal().toString();
        System.out.println(username);
        Sys_User sys_user = sysUserMapper.findSysUserByUsername(username);
        System.out.println(sys_user);
        //RBAC基于角色的权限控制
        //根据用户id查询对应角色信息
        List<Sys_Role> roleList = sysRoleMapper.findSysRoleByUserId(sys_user.getUserId());
        //角色用Set来装，目的是去重，还有info对象中需要Set格式
        Set<String> roleSets = new HashSet<>();
        //获取角色ID
        List<Integer> role_ids = new ArrayList<>();
        for (Sys_Role role:roleList) {
            roleSets.add(role.getRoleTitle());
            role_ids.add(role.getRoleId());
        }
        //根据账户id查询对应的角色
        //根据对应的角色id查询对应权限
        Set<String> sys_menuSets = new HashSet<>();
        List<Sys_Menu> sys_menus = sysRoleMapper.findMenuListByRoleId(role_ids);
        for (Sys_Menu sm:sys_menus) {
            sys_menuSets.add(sm.getTitle());
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //根据账户查询数据库中对应账户的角色
        info.setRoles(roleSets);
        //根据账户查询数据库中对应账户的权限
        info.setStringPermissions(sys_menuSets);
        return info;
    }

    //认证/登录验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //接收客户端的账户
        String username =  authenticationToken.getPrincipal().toString();
        System.out.println(username);
        Sys_User sys_user = sysUserMapper.findSysUserByUsername(username);
        System.out.println(sys_user);
        if(sys_user != null ){
            //客户端账户不需要比较了，密码做比较：
            //客户端的密码：123456 - 加盐 - MD5  - 散列 - > 5d8dd839e07001786cd291af98f1e6e 数据库中查询的密码：25d8dd839e07001786cd291af98f1e6e
            //将数据库中查询出来的账户与密码和客户端传过来的做比较

            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(sys_user.getUsername(),sys_user.getPassword(),"myRealm");
            //加盐
            System.out.println(sys_user.getPassword());
            System.out.println(info);
            info.setCredentialsSalt(ByteSource.Util.bytes(sys_user.getSalt()));
            System.out.println(info);

            return info;
        }
        return null;
    }

    /**
     * 自定义密码验证匹配器
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        setCredentialsMatcher(new SimpleCredentialsMatcher() {
            @Override
            public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                System.out.println(token);
                SimpleAuthenticationInfo info = (SimpleAuthenticationInfo) authenticationInfo;
                // 获取明文密码及密码盐
                String password = String.valueOf(token.getPassword());
                String salt = CodecSupport.toString(info.getCredentialsSalt().getBytes());
                //将密码加密加盐散列
                SimpleHash encrypt = new SimpleHash("md5",password,salt,3);
                //判断数据库密码与客户端密码是否一致
                if(encrypt.toString().equals(info.getCredentials())){
                    return true;
                }
                return false;
            }
        });
    }

}
